package cc.rengu.igas.channel.wechat.core.service.trans;

import cc.rengu.igas.channel.wechat.common.constant.*;
import cc.rengu.igas.channel.wechat.common.enums.RespCodeEnum;
import cc.rengu.igas.channel.wechat.core.model.ChnlBizResponse;
import cc.rengu.igas.channel.wechat.core.service.base.AuthService;
import cc.rengu.igas.channel.wechat.facade.request.GetJsapiTicketRequest;
import cc.rengu.igas.channel.wechat.facade.response.GetJsapiTicketResponse;
import cc.rengu.jradp.schedule.Schedule;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.KeyInfoMapper;
import cc.rengu.oltp.service.common.dao.SecPlanInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.KeyInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SecPlanInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.DstChannelCfg;
import cc.rengu.oltp.service.common.entity.KeyInfo;
import cc.rengu.oltp.service.common.entity.SecPlanInfo;
import cc.rengu.oltp.service.common.entity.SysParam;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.*;
import cc.rengu.utility.communication.HttpMethod;
import com.alibaba.fastjson.JSONObject;

import java.security.MessageDigest;
import java.util.*;

/**
 * 获取公众号ticket
 */
public class GetJsapiTicketService extends AuthService {
    @Override
    protected Object beforeBizProcess() throws Exception {
        GetJsapiTicketRequest getJsapiTicketRequest = new GetJsapiTicketRequest();
        ConvertUtil.convertOutput(getJsapiTicketRequest);
        rglog.debug("beforeBizProcess ---- getJsapiTicketRequest = <{}>", JSONObject.toJSONString(getJsapiTicketRequest));
        return getJsapiTicketRequest;
    }

    @Override
    protected ChnlBizResponse callBizService(Object request) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        int iReturnCode = 0;
        String signKey = null;
        GetJsapiTicketResponse getJsapiTicketResponse = new GetJsapiTicketResponse();
        GetJsapiTicketRequest getJsapiTicketRequest = (GetJsapiTicketRequest) request;
        rglog.debug("callBizService ---- getJsapiTicketRequest = <{}>", JSONObject.toJSONString(getJsapiTicketRequest));
        String instId = getJsapiTicketRequest.getHeader().getInstId();
        DstChannelCfg dstChannelCfg = (DstChannelCfg) xmlTreeUtil.getXmlTreeObjectValue(WechatTreeNodeConstant.DST_CHANNEL_CFG, DstChannelCfg.class);
        /*从redis缓存获取ticket*/
        String jsapiTicket = RedisUtil.onceGet(WechatParamConstant.TICKET_CACHE_NAME + dstChannelCfg.getSubAppId());
        if (jsapiTicket == null) {
            jsapiTicket = getJsapiTicket(dstChannelCfg);
        }
        rglog.debug("appid = <{}>", dstChannelCfg.getSubAppId());
        String nonceStr = RandomUtil.getUUID();
        String timestamp = Long.toString(System.currentTimeMillis() / 1000);
        rglog.debug("jsapiTicket=<{}>,nonceStr=<{}>,timestamp=<{}>,pageUrl=<{}>", jsapiTicket, nonceStr, timestamp, getJsapiTicketRequest.getPageUrl());
        String sign = genSignature(jsapiTicket, nonceStr, timestamp, getJsapiTicketRequest.getPageUrl());
        getJsapiTicketResponse.setJsapiTicket(jsapiTicket);
        getJsapiTicketResponse.setNonceStr(nonceStr);
        getJsapiTicketResponse.setTimestamp(timestamp);
        getJsapiTicketResponse.setWechatSign(sign);
        getJsapiTicketResponse.setRespCode(RespCodeEnum.TRANS_SUCCESS.getRespCode());
        getJsapiTicketResponse.setRespDesc(RespCodeEnum.TRANS_SUCCESS.getRespDesc());
        ChnlBizResponse chnlBizResponse = new ChnlBizResponse();
        chnlBizResponse.setResult(getJsapiTicketResponse);
        rglog.debug("response = <{}>", JSONObject.toJSONString(chnlBizResponse));
        return chnlBizResponse;
    }

    @Override
    protected void afterBizProcess(ChnlBizResponse response) throws Exception {
        GetJsapiTicketResponse getJsapiTicketResponse = (GetJsapiTicketResponse) response.getResult();
        ConvertUtil.convertInput(getJsapiTicketResponse);
    }

    /**
     * 根据配置拼装URL地址
     *
     * @param transUrl    URL地址
     * @param urlParamMap URL参数集合
     * @return URL地址
     */
    private String generateHttpUrl(String transUrl, Map<String, String> urlParamMap) {
        String requestUrlParam = "";
        Iterator<String> it = urlParamMap.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next();
            requestUrlParam += key + "=" + urlParamMap.get(key) + "&";
        }
        return transUrl + requestUrlParam.substring(0, requestUrlParam.length() - 1);
    }

    private String getAccessToken(DstChannelCfg dstChannelCfg) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String secret = null;
        /*清除调用失败时的错误信息*/
        xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.WX_ERR_CODE);
        xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.WX_ERR_MSG);
        /* 根据通道安全计划获取安全计划 */
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        rglog.debug("instId = <{}>,secPlanId = <{}>", dstChannelCfg.getInstId(), dstChannelCfg.getSecPlanId());
        List<SecPlanInfo> secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(dstChannelCfg.getInstId(), dstChannelCfg.getSecPlanId());
        if (null != secPlanInfoList && !secPlanInfoList.isEmpty()) {
            Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(WechatParamConstant.SECRET_KEY_TYPE)).findFirst();
            if (secPlanInfoOpt.isPresent()) {
                KeyInfoMapper keyInfoMapper = new KeyInfoMapperImpl();
                rglog.debug("access_token-key_info = <{}>", secPlanInfoOpt.get().getInstId() + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
                KeyInfo keyInfo = keyInfoMapper.selectKeyInfoByPrimaryKey(secPlanInfoOpt.get().getInstId() + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
                if (keyInfo != null) {
                    secret = keyInfo.getKeyValue();
                }
            }
        }
        if (secret == null) {
            rglog.error("获取服务商secret失败");
            throw new BizException(RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespCode(), RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespDesc());
        }
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, WechatTxnNumConstant.getaccesstoken);
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("appid", dstChannelCfg.getSubAppId());
        paramMap.put("secret", secret);
        paramMap.put("grant_type", WechatParamConstant.DEFAULT_WECHAT_TOKEN_GRANT_TYPE);
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParam = sysParamService.getSysParamInfo(dstChannelCfg.getInstId(),
                WechatConfigConstant.WECHAT_CONF_NODE, WechatConfigConstant.WECHAT_ACCESS_TOKEN_URL);
        if (sysParam == null) {
            throw new BizException(RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespCode(), RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespDesc());
        }
        String requestUrl = generateHttpUrl(sysParam.getParamValue(), paramMap);
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE, requestUrl);
        Schedule schedule = new Schedule();
        int iReturnCode = schedule.callService(WechatServiceConstant.WECHAT_PUBLIC_CLIENT_SERVICE, HttpMethod.GET);
        if (xmlTreeUtil.isExist(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE)) {
            xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE);
        }
        if (0 != iReturnCode) {
            rglog.error("调用微信公众号服务失败，iReturnCode:<{}>", iReturnCode);
            throw new BizException(RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespCode(), RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespDesc());
        }
        /* 判断调用微信是否成功 */
        String errcode = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_ERR_CODE);
        String errmsg = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_ERR_MSG);
        if (null != errcode) {
            rglog.error("调用微信公众号服务失败,errcode:<{}>,errmsg:<{}>", errcode, errmsg);
            throw new BizException(errcode, errmsg);
        }
        /*清除调用成功时的信息*/
        xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.WX_ERR_CODE);
        xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.WX_ERR_MSG);
        String accessToken = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.ACCESS_TOKEN);
        String tokenExpiryTime = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.ACCESS_TOKEN);
        /*加载到redis缓存*/
        RedisUtil.onceSet(WechatParamConstant.ACCESS_TOKEN_CACHE_NAME + dstChannelCfg.getSubAppId(), accessToken, WechatParamConstant.ACCESS_TOKEN_EXPIRY_TIME);
        return accessToken;
    }

    private String getJsapiTicket(DstChannelCfg dstChannelCfg) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /*从redis缓存获取accessToken*/
        String accessToken = RedisUtil.onceGet(WechatParamConstant.ACCESS_TOKEN_CACHE_NAME + dstChannelCfg.getSubAppId());
        if (accessToken == null) {
            accessToken = getAccessToken(dstChannelCfg);
            rglog.debug("重新获取access_token = <{}>", accessToken);
        }
        rglog.debug("access_token = <{}>", accessToken);
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, WechatTxnNumConstant.getticket);
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("access_token", accessToken);
        paramMap.put("type", WechatParamConstant.WECHAT_TICKET_TYPE);
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParam = sysParamService.getSysParamInfo(dstChannelCfg.getInstId(),
                WechatConfigConstant.WECHAT_CONF_NODE, WechatConfigConstant.WECHAT_JSSDK_TICKET_URL);
        if (sysParam == null) {
            throw new BizException(RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespCode(), RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespDesc());
        }
        String requestUrl = generateHttpUrl(sysParam.getParamValue(), paramMap);
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE, requestUrl);
        Schedule schedule = new Schedule();
        int iReturnCode = schedule.callService(WechatServiceConstant.WECHAT_PUBLIC_CLIENT_SERVICE, HttpMethod.GET);
        if (xmlTreeUtil.isExist(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE)) {
            xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE);
        }
        if (0 != iReturnCode) {
            rglog.error("调用微信公众号服务失败，iReturnCode:<{}>", iReturnCode);
            throw new BizException(RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespCode(), RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespDesc());
        }
        /* 判断调用微信是否成功 */
        String errcode = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_ERR_CODE);
        String errmsg = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_ERR_MSG);
        String jsapiTicket;
        if (!"0".equals(errcode) && !"40001".equals(errcode)) {
            rglog.error("调用微信公众号服务失败,errcode:<{}>,errmsg:<{}>", errcode, errmsg);
            throw new BizException(errcode, errmsg);
        } else if ("40001".equals(errcode)) {
            rglog.debug("accessToken失效重新获取！");
            accessToken = getAccessToken(dstChannelCfg);
            rglog.debug("重新获取access_token = <{}>", accessToken);
            jsapiTicket = getJsapiTicket(dstChannelCfg);
            rglog.debug("jsapiTicket：<{}>", jsapiTicket);
        } else {
            jsapiTicket = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.JSAPI_TICKET);
            /*加载到redis缓存*/
            RedisUtil.onceSet(WechatParamConstant.TICKET_CACHE_NAME + dstChannelCfg.getSubAppId(), jsapiTicket, WechatParamConstant.ACCESS_TOKEN_EXPIRY_TIME);
        }
        return jsapiTicket;
    }

    private String genSignature(String jsapiTicket, String nonceStr, String timestamp, String pageUrl) throws Exception {
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("jsapi_ticket=" + jsapiTicket + "&");
        arrayList.add("noncestr=" + nonceStr + "&");
        arrayList.add("timestamp=" + timestamp + "&");
        arrayList.add("url=" + pageUrl + "&");
        String[] strArr = arrayList.toArray(new String[arrayList.size()]);
        Arrays.sort(strArr);
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < strArr.length; i++) {
            stringBuilder.append(strArr[i]);
        }
        String sign = stringBuilder.substring(0, stringBuilder.toString().length() - 1);
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
        messageDigest.reset();
        messageDigest.update(sign.getBytes("UTF-8"));
        String wechatSign = ByteUtil.bytesToHexString(messageDigest.digest()).toLowerCase();
        return wechatSign;
    }
}
